package com.etone.fileServer.service;

import com.etone.fileServer.dto.compress.CompressDTO;
import com.etone.fileServer.dto.compress.DecompressDTO;
import com.etone.fileServer.error.CustomParameterizedException;
import com.etone.fileServer.utils.SignUtil;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: sovy
 * @date: 2020/11/4
 * @description:
 */
@Service
public class CompressService {
    /**
     * 压缩文件
     *
     * @param compressDTO
     * @param timestamp
     * @param sign
     * @return
     */
    public void compress(CompressDTO compressDTO, Long timestamp, String sign) {
        if (CollectionUtils.isEmpty(compressDTO.getFiles())) {
            throw new CustomParameterizedException("没有压缩文件");
        }
        SignUtil.checkSignThrow(compressDTO, timestamp, sign);
        File destFile = new File(compressDTO.getDestFile());
        if (!destFile.exists()) {
            Map<String, File> fileMap = new HashMap<>();
            compressDTO.getFiles().forEach(fileStr -> {
                File file = new File(fileStr);
                if (file.exists()) {
                    getFiles(file, null, fileMap);
                }

            });
            ZipArchiveOutputStream zaos = null;
            try {
                zaos = new ZipArchiveOutputStream(destFile);
                zaos.setUseZip64(Zip64Mode.AsNeeded);
                for (String mapKey : fileMap.keySet()) {
                    File file = fileMap.get(mapKey);
                    if (file.exists()) {
                        InputStream is = null;
                        try {
                            is = new BufferedInputStream(new FileInputStream(file));
                            ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(file, mapKey);
                            zaos.putArchiveEntry(zipArchiveEntry);
                            byte[] buffer = new byte[1024 * 5];
                            int len = -1;
                            while ((len = is.read(buffer)) != -1) {
                                //把缓冲区的字节写入到ZipArchiveEntry
                                zaos.write(buffer, 0, len);
                            }
                            //Writes all necessary data for this entry.
                            zaos.closeArchiveEntry();
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        } finally {
                            if (is != null) {
                                is.close();
                            }
                        }

                    }

                }
            } catch (Exception e) {
                if (zaos != null) {
                    try {
                        zaos.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 解压文件
     *
     * @param decompressDTO
     * @param timestamp
     * @param sign
     */
    public void decompress(DecompressDTO decompressDTO, Long timestamp, String sign) {
        SignUtil.checkSignThrow(decompressDTO, timestamp, sign);
        String sourceFile = decompressDTO.getSourceFile();
        String destPath = decompressDTO.getDestPath();
        File compressFile = new File(sourceFile);
        if (compressFile.exists()) {
            InputStream is = null;
            ArchiveInputStream zais = null;
            try {

                if (sourceFile.toLowerCase().endsWith("tar.gz")) {
                    is = new GzipCompressorInputStream(Files.newInputStream(Paths.get(sourceFile)));
                    zais = new TarArchiveInputStream(is, "GBK");
                } else if ("tar".equals(FilenameUtils.getExtension(sourceFile).toLowerCase())) {
                    is = new FileInputStream(compressFile);
                    zais = new TarArchiveInputStream(is, "GBK");
                } else {
                    is = new FileInputStream(compressFile);
                    zais = new ZipArchiveInputStream(is, "GBK");
                }

                ArchiveEntry archiveEntry = null;
                try {
                    while ((archiveEntry = zais.getNextEntry()) != null) {

                        //获取文件名
                        String entryFileName = archiveEntry.getName();

                        //构造解压出来的文件存放路径
                        String entryFilePath = destPath + "/" + entryFileName;
                        if (!archiveEntry.isDirectory()) {
                            byte[] content = new byte[(int) archiveEntry.getSize()];
                            zais.read(content);
                            OutputStream os = null;
                            try {
                                //把解压出来的文件写到指定路径
                                File entryFile = new File(entryFilePath.trim());
                                os = new BufferedOutputStream(new FileOutputStream(entryFile));
                                os.write(content);
                            } catch (IOException e) {
                                throw new IOException(e);
                            } finally {
                                if (os != null) {
                                    os.flush();
                                    os.close();
                                }
                            }
                        } else {
                            File file = new File(entryFilePath);
                            if (!file.exists()) {
                                file.mkdir();
                            }
                        }

                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            } catch (FileNotFoundException e) {
                try {
                    if (zais != null) {
                        zais.close();
                    }
                    if (is != null) {
                        is.close();
                    }
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * 获取所有文件
     *
     * @param file
     * @param parent
     * @param fileMap
     */
    public void getFiles(File file, String parent, Map<String, File> fileMap) {
        String name = file.getName();
        if (parent != null) {
            //设置在zip包里的相对路径
            name = parent + "/" + name;
        }
        if (file.isFile()) {
            fileMap.put(name, file);
        } else if (file.isDirectory()) {
            for (File children : file.listFiles()) {
                getFiles(children, name, fileMap);
            }
        }
    }
}
